package chapt07.operations;
// Triangle.java
// OpenGL SuperBible
// Demonstrates Imaging Operations
// Program by Richard S. Wright Jr.

import java.io.IOException;

import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;

import com.jogamp.opengl.util.texture.spi.TGAImage;

import java.nio.ByteBuffer;

public class Operations
{
	static GLU glu = new GLU();
	
	//////////////////////////////////////////////////////////////////
	// Module globals to save source image data
	static TGAImage tgaImage;
	static ByteBuffer pImage = null;
	static int iWidth, iHeight, iComponents;
	static int eFormat;
	
	// Global variable to store desired drawing mode
	static int iRenderMode = 1;

	static int processMenuValue = 1;
		
	///////////////////////////////////////////////////////////////////////////////
	// Reset flags as appropriate in response to menu selections
	public static void processMenu(GL2 gl2, int value)
	{
		if(value == 0)
		{
			try
			{
				tgaImage.write("ScreenShot.tga");
			}
			catch(IOException e)
			{
				System.exit(0);
			}
		}
		else
		{
			// Change render mode index to match menu entry index
			iRenderMode = value;
		}
	}
	
	//////////////////////////////////////////////////////////////////
	// This function does any needed initialization on the rendering
	// context.
	protected static void setupRC(GL2 gl2, int width, int height)
	{
		// Black background
		gl2.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		
		// Load the horse image
		gl2.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
		try
		{
			tgaImage = TGAImage.read(Operations.class.getResourceAsStream("horse.tga"));
			iWidth = tgaImage.getWidth();
			iHeight = tgaImage.getHeight();
			eFormat = tgaImage.getGLFormat();
			pImage = tgaImage.getData();
		}
		catch(IOException e)
		{
			System.exit(0);
		}
	}
	
	///////////////////////////////////////////////////////////////////////        
	// Called to draw scene
	protected static void renderScene(GL2 gl2, int w, int h)
	{
		processMenu(gl2, processMenuValue);
		
		int iViewport[] = new int[4];
		byte pModifiedBytes[] = null;
		float invertMap[] = new float[256];
		int i;
		
		// Clear the window with current clearing color
		gl2.glClear(GL2.GL_COLOR_BUFFER_BIT);
		
		// Current Raster Position always at bottom left hand corner of window
		gl2.glRasterPos2i(0, 0);
		
		// Do image operation, depending on rendermode index
		switch(iRenderMode)
		{
			case 2: // Flip the pixels
				gl2.glPixelZoom(-1.0f, -1.0f);
				gl2.glRasterPos2i(iWidth, iHeight);
				break;
			
			case 3: // Zoom pixels to fill window
				gl2.glGetIntegerv(GL2.GL_VIEWPORT, iViewport, 0);
				gl2.glPixelZoom((float)iViewport[2] / (float)iWidth, (float)iViewport[3] / (float)iHeight);
				break;
			
			case 4: // Just Red
				gl2.glPixelTransferf(GL2.GL_RED_SCALE, 1.0f);
				gl2.glPixelTransferf(GL2.GL_GREEN_SCALE, 0.0f);
				gl2.glPixelTransferf(GL2.GL_BLUE_SCALE, 0.0f);
				break;
				
			case 5: // Just Green
				gl2.glPixelTransferf(GL2.GL_RED_SCALE, 0.0f);
				gl2.glPixelTransferf(GL2.GL_GREEN_SCALE, 1.0f);
				gl2.glPixelTransferf(GL2.GL_BLUE_SCALE, 0.0f);
				break;
				
			case 6: // Just Blue
				gl2.glPixelTransferf(GL2.GL_RED_SCALE, 0.0f);
				gl2.glPixelTransferf(GL2.GL_GREEN_SCALE, 0.0f);
				gl2.glPixelTransferf(GL2.GL_BLUE_SCALE, 1.0f);
				break;
				
			case 7: // Black & White, more tricky
				// First draw image into color buffer
				gl2.glDrawPixels(iWidth, iHeight, eFormat, GL2.GL_UNSIGNED_BYTE, pImage);
				
				// Allocate space for the luminance map
				pModifiedBytes = new byte[iWidth * iHeight];
				
				// Scale colors according to NSTC standard
				gl2.glPixelTransferf(GL2.GL_RED_SCALE, 0.3f);
				gl2.glPixelTransferf(GL2.GL_GREEN_SCALE, 0.59f);
				gl2.glPixelTransferf(GL2.GL_BLUE_SCALE, 0.11f);
				
				// Read pixles into buffer (scale above will be applied)
				gl2.glReadPixels(0, 0, iWidth, iHeight, GL2.GL_LUMINANCE, GL2.GL_UNSIGNED_BYTE, ByteBuffer.wrap(pModifiedBytes));
				
				// Return color scaling to normal
				gl2.glPixelTransferf(GL2.GL_RED_SCALE, 1.0f);
				gl2.glPixelTransferf(GL2.GL_GREEN_SCALE, 1.0f);
				gl2.glPixelTransferf(GL2.GL_BLUE_SCALE, 1.0f);
				break;
				
			case 8: // Invert colors
				invertMap[0] = 1.0f;
				for(i = 1; i < 256; i++)
					invertMap[i] = 1.0f - (1.0f / 255.0f * (float)i);
				
				gl2.glPixelMapfv(GL2.GL_PIXEL_MAP_R_TO_R, 255, invertMap, 0);
				gl2.glPixelMapfv(GL2.GL_PIXEL_MAP_G_TO_G, 255, invertMap, 0);
				gl2.glPixelMapfv(GL2.GL_PIXEL_MAP_B_TO_B, 255, invertMap, 0);
				gl2.glPixelTransferi(GL2.GL_MAP_COLOR, 1);
				break;
			
			case 1: // Just do a plain old image copy
			default:
				// This line intentionally left blank
				break;
		}
		
		// Do the pixel draw
		if(pModifiedBytes == null)
			gl2.glDrawPixels(iWidth, iHeight, eFormat, GL2.GL_UNSIGNED_BYTE, pImage);
		else
		{
			gl2.glDrawPixels(iWidth, iHeight, GL2.GL_LUMINANCE, GL2.GL_UNSIGNED_BYTE, ByteBuffer.wrap(pModifiedBytes));
			pModifiedBytes = null;
		}
		
		// Reset everyting to default
		gl2.glPixelTransferi(GL2.GL_MAP_COLOR, 0);
		gl2.glPixelTransferf(GL2.GL_RED_SCALE, 1.0f);
		gl2.glPixelTransferf(GL2.GL_GREEN_SCALE, 1.0f);
		gl2.glPixelTransferf(GL2.GL_BLUE_SCALE, 1.0f);
		gl2.glPixelZoom(1.0f, 1.0f);                    // No Pixel Zooming
		
		// Do the buffer Swap
		gl2.glFlush();
	}
	
	public static void changeSize(GL2 gl2, int w, int h)
	{
	    // Prevent a divide by zero, when window is too short
	    // (you cant make a window of zero width).
		if(h == 0)
			h = 1;

		gl2.glViewport(0, 0, w, h);
		
		// Reset the coordinate system before modifying
		gl2.glMatrixMode(GL2.GL_PROJECTION);
		gl2.glLoadIdentity();
		
		// Set the clipping volume
		glu.gluOrtho2D(0.0f, (float)w, 0.0, (float)h);
		
		gl2.glMatrixMode(GL2.GL_MODELVIEW);
		gl2.glLoadIdentity();		
	}
}